/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.msu.client.register;

import io.iec.edp.caf.commons.runtime.CafEnvironment;
import io.iec.edp.caf.commons.transaction.JpaTransaction;
import io.iec.edp.caf.commons.transaction.TransactionPropagation;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import io.iec.edp.caf.commons.utils.StringUtils;
import io.iec.edp.caf.msu.api.ServiceUnitAwareService;
import io.iec.edp.caf.msu.api.client.ServiceRegistry;
import io.iec.edp.caf.msu.api.entity.ServiceUnitInfo;
import io.iec.edp.caf.msu.api.entity.ServiceUnitRegisterInfo;
import io.iec.edp.caf.msu.common.domain.entity.GspAppServerEntity;
import io.iec.edp.caf.msu.common.domain.entity.GspSuEntity;
import io.iec.edp.caf.msu.common.domain.repository.AppServerRepository;
import io.iec.edp.caf.msu.common.domain.repository.SuRepository;
import io.iec.edp.caf.msu.common.utils.NetUtil;
import io.iec.edp.caf.tenancy.api.context.MultiTenantContextInfo;
import io.iec.edp.caf.tenancy.core.context.MultiTenantContextHolder;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.springframework.core.env.Environment;

import javax.annotation.PreDestroy;
import java.util.List;

/**
 * DB部署，采用数据表GSPAppServerInstance和GSPSUInstance表作为服务中心，实现服务注册
 *
 * @author wangyandong
 * @date 2021/09/10 13:31
 */
@Slf4j
public class DbRegisterImpl implements ServiceRegistry {

    private AppServerRepository appRepo;
    private SuRepository suRepo;
    private ServiceUnitAwareService suAware;

    private boolean enableSSL;

    public DbRegisterImpl(AppServerRepository appRepo, SuRepository suRepo, ServiceUnitAwareService suAware) {
        this.appRepo = appRepo;
        this.suRepo = suRepo;
        this.suAware = suAware;
        this.enableSSL = enableSSL();
    }

    @Override
    public Boolean register(ServiceUnitRegisterInfo registerInfo) {
        log.info("ServiceCenter(DataBase) start to register su");

        try {
            //直接注册到主库
            MultiTenantContextInfo contextInfo = new MultiTenantContextInfo();
            contextInfo.setMasterDb(true);
            MultiTenantContextHolder.set(contextInfo);

            JpaTransaction transaction = JpaTransaction.getTransaction();
            try {
                transaction.begin(TransactionPropagation.REQUIRES_NEW);
                //执行注册
                doRegister(registerInfo);

                transaction.commit();
            } catch (Throwable e) {
                transaction.rollback();
                log.error("ServiceCenter(DataBase) failed to register su");
                throw new RuntimeException(e);
            }
        } finally {
            MultiTenantContextHolder.set(null);
        }

        log.info("ServiceCenter(DataBase) finish register su");
        return true;
    }

    @Override
    @PreDestroy
    public Boolean unRegister() {
        log.info("ServiceCenter(DataBase) start to unregister su");

        //依次注销su、实例信息
        String appName = getApplicationName();
        List<String> suNames = this.suAware.getEnabledServiceUnits();
        for (var suName : suNames) {
            this.suRepo.deleteByAppAndSu(appName, suName.toLowerCase());
        }
        this.appRepo.deleteByAppName(appName);

        log.info("ServiceCenter(DataBase) finish unregister su");
        return true;
    }

    @Override
    public Boolean register(String namespace, ServiceUnitRegisterInfo registerInfo) {
        //DB部署，数据库隔离，namespace不需要
        return register(registerInfo);
    }

//    @Override
//    public Boolean unRegister(String namespace) {
//        //DB部署，数据库隔离，namespace不需要
//        return unRegister();
//    }

    /**
     * 依次注册实例、su信息
     *
     * @param registerInfo 注册信息
     */
    private void doRegister(ServiceUnitRegisterInfo registerInfo) {
        String appName = getApplicationName();

        //清理su信息、实例信息
        this.suRepo.deleteByApp(appName);       //此处是一重保险，避免非优雅停机导致数据未清理
        this.appRepo.deleteByAppName(appName);

        //注册实例信息、su信息
        //注册实例信息
        String ip = getLocalIP();
        String port = getLocalPort();
        String url = this.enableSSL ? String.format("https://%s:%s", ip, port) : String.format("http://%s:%s", ip, port);
        GspAppServerEntity appEntity = new GspAppServerEntity();
        appEntity.setIp(ip);
        appEntity.setPort(port);
        appEntity.setAppName(appName);
        appEntity.setAppUrl(url);
        this.appRepo.save(appEntity);
        //注册su信息
        List<ServiceUnitInfo> suInfos = registerInfo.getServiceUnitInfo();
        if (suInfos != null) {
            for (ServiceUnitInfo su : suInfos) {
                if (su != null) {
                    GspSuEntity suEntity = new GspSuEntity();
                    suEntity.setApp(appName);
                    suEntity.setSu(su.getName().toLowerCase());
                    this.suRepo.save(suEntity);
                }
            }
        }
    }

    /**
     * 返回应用程序名称
     */
    private String getApplicationName() {
        Environment env = SpringBeanUtils.getBean(Environment.class);
        String applicationName = env.getProperty("spring.application.name", "");
        if ("".equals(applicationName)) {
            applicationName = env.getProperty("msu.serviceName", "iGIX-Server");
        }
        return applicationName;
    }

    /**
     * 获取当前机器的IP
     */
    private String getLocalIP() {
        //优先获取外网地址
        String ip = NetUtil.INTERNET_IP;
        //取不到外网地址，则返回内网地址
        if (ip == null || "".equals(ip))
            ip = NetUtil.INTRANET_IP;
        return ip;
    }

    /**
     * 获取当前tomcat机器的端口号
     */
    private String getLocalPort() {
        Environment environment = CafEnvironment.getEnvironment();
        return environment.getProperty("local.server.port");
    }

    /**
     * 是否开启https
     */
    private boolean enableSSL() {
        Environment environment = CafEnvironment.getEnvironment();
        return !StringUtils.isEmpty(environment.getProperty("server.ssl.key-store"));
    }
}
